package com.feiya.pianoscore;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

import androidx.core.content.ContextCompat;

public class AubioHelper
{
    private AubioHelper()
    {

    }

    private static final AubioHelper single = new AubioHelper();
    public static AubioHelper getInstance() {      return single;
    }
    final static String TAG = "Piano";
    private int         sampleRate = 0;
    private int         bufferSize = 0;
    private int         readSize = 0;
    private int         amountRead = 0;
    private float[]     buffer = null;
    private short[]     intermediaryBuffer = null;

    /* These variables are used to store pointers to the C objects so JNI can keep track of them */
    public long ptr = 0;
    public long input = 0;
    public long pitch = 0;
    public long notes = 0;
    public long pitchPtr = 0;
    public long pitchInput = 0;
    public boolean      isRecording = false;
    private AudioRecord audioRecord = null;
    Thread audioThread;

    static
    {
        System.loadLibrary("aubio");
        System.loadLibrary("notes");
    }

    public void init() {
        Log.d(TAG, "AubioHelper init");
        sampleRate = 44100;
        bufferSize = 4096;
        readSize = bufferSize / 4;
        buffer = new float[readSize];
        intermediaryBuffer = new short[readSize];
    }

    public interface ActionRecordRun {
        void perform();
    }
    ActionRecordRun m_ActionRecordRun;
    public void startRecord(Context context, ActionRecordRun onRecordRun)
    {
        m_ActionRecordRun = onRecordRun;

        initPitch(sampleRate, bufferSize);
        //initNotes(sampleRate, bufferSize);
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED)
        {
            audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, AudioFormat.CHANNEL_IN_DEFAULT,
                    AudioFormat.ENCODING_PCM_16BIT, bufferSize);
            audioRecord.startRecording();
            audioThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //Log.d(TAG, "RecordRun!");
                    if(m_ActionRecordRun != null)
                    {
                        m_ActionRecordRun.perform();
                    }
                }
            }, "Tuner Thread");
            audioThread.start();
        }
        else
        {
            Log.e(TAG,"no permission!");
        }
    }

    public void stopRecord() {

        m_ActionRecordRun = null;
        audioRecord.stop();
        cleanupPitch();
        //cleanupNotes();
    }

    public float getPitch(){
        if(audioRecord == null){
            Log.e(TAG, "GetPitch audioRecord is null" );
            return 0;
        }
        amountRead = audioRecord.read(intermediaryBuffer, 0, readSize);
        buffer = shortArrayToFloatArray(intermediaryBuffer);
        final float frequency = getPitch(buffer);

        return frequency;
    }

    public float[] getNotes(){
        if(audioRecord == null){
            Log.e(TAG, "getNotes audioRecord is null" );
            return null;
        }
        amountRead = audioRecord.read(intermediaryBuffer, 0, readSize);
        buffer = shortArrayToFloatArray(intermediaryBuffer);
        final float[] notes = getNotes(buffer);
        //LogArr("getNotes:",notes);
        return notes;
    }
    public void LogArr(String msg,float[] arr){
            String str = "";
            for (int i=0;i<arr.length;i++){
                str+= arr[i]+",";
            }
        Log.d(TAG,msg+ str);
    }

    private float[] shortArrayToFloatArray(short[] array) {
        float[] fArray = new float[array.length];
        for (int i = 0; i < array.length; i++) {
            fArray[i] = (float) array[i];
        }
        return fArray;
    }

    private native float    getPitch(float[] input);
    private native void     initPitch(int sampleRate, int B);
    private native void     cleanupPitch();
    private native void     initNotes(int sampleRate, int B);
    private native float[]    getNotes(float[] input);
    private native void     cleanupNotes();
}
